 /*******************************************************************************
  * Copyright (c) 2004, 2005 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.internal.dialogs;

 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.core.runtime.jobs.Job;
 import org.eclipse.jface.action.Action;
 import org.eclipse.jface.action.IAction;
 import org.eclipse.jface.action.ToolBarManager;
 import org.eclipse.jface.resource.ImageDescriptor;
 import org.eclipse.jface.resource.JFaceResources;
 import org.eclipse.jface.viewers.TreeViewer;
 import org.eclipse.osgi.util.NLS;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.accessibility.AccessibleAdapter;
 import org.eclipse.swt.accessibility.AccessibleEvent;
 import org.eclipse.swt.events.DisposeEvent;
 import org.eclipse.swt.events.DisposeListener;
 import org.eclipse.swt.events.FocusEvent;
 import org.eclipse.swt.events.FocusListener;
 import org.eclipse.swt.events.KeyAdapter;
 import org.eclipse.swt.events.KeyEvent;
 import org.eclipse.swt.graphics.Color;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.layout.GridLayout;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Text;
 import org.eclipse.swt.widgets.ToolBar;
 import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.internal.WorkbenchMessages;
 import org.eclipse.ui.plugin.AbstractUIPlugin;
 import org.eclipse.ui.progress.WorkbenchJob;

 /**
  * A simple control that provides a text widget and a tree viewer. The contents
  * of the text widget are used to drive a PatternFilter that is on the viewer.
  *
  * @see org.eclipse.ui.internal.dialogs.PatternFilter
  * @since 3.0
  */
 public class FilteredTree extends Composite {

     protected Text filterText;
     
     private ToolBarManager filterToolBar;

     protected TreeViewer treeViewer;

     private Composite filterParent;

     private PatternFilter patternFilter;
     
     private PreferenceNodeFilter preferenceFilter;

     private FocusListener listener;

     private static final String CLEAR_ICON = "org.eclipse.ui.internal.dialogs.CLEAR_ICON"; //$NON-NLS-1$

     private static final String DCLEAR_ICON = "org.eclipse.ui.internal.dialogs.DCLEAR_ICON"; //$NON-NLS-1$

     protected String initialText = ""; //$NON-NLS-1$

     private String cachedTitle;
     
     //The job for refreshing the tree
 private Job refreshJob;
    
     static {
         ImageDescriptor descriptor = AbstractUIPlugin
                 .imageDescriptorFromPlugin(PlatformUI.PLUGIN_ID,
                         "$nl$/icons/full/etool16/clear_co.gif"); //$NON-NLS-1$
 if (descriptor != null) {
             JFaceResources.getImageRegistry().put(CLEAR_ICON, descriptor);
         }
         descriptor = AbstractUIPlugin.imageDescriptorFromPlugin(
                 PlatformUI.PLUGIN_ID, "$nl$/icons/full/dtool16/clear_co.gif"); //$NON-NLS-1$
 if (descriptor != null) {
             JFaceResources.getImageRegistry().put(DCLEAR_ICON, descriptor);
         }
     }

     /**
      * Create a new instance of the receiver. It will be created with a default
      * pattern filter.
      *
      * @param parent
      * the parent composite
      * @param treeStyle
      * the SWT style bits to be passed to the tree viewer
      */
     public FilteredTree(Composite parent, int treeStyle) {
         this(parent, treeStyle, new PatternFilter());
     }

     /**
      * Create a new instance of the receiver.
      *
      * @param parent
      * parent <code>Composite</code>
      * @param treeStyle
      * the style bits for the <code>Tree</code>
      * @param filter
      * the filter to be used
      */
     public FilteredTree(Composite parent, int treeStyle, PatternFilter filter) {
         super(parent, SWT.NONE);
         patternFilter = filter;
         GridLayout layout = new GridLayout();
         layout.marginHeight = 0;
         layout.marginWidth = 0;
         setLayout(layout);

         filterParent = new Composite(this, SWT.NONE);
         GridLayout filterLayout = new GridLayout();
         filterLayout.numColumns = 2;
         filterLayout.marginHeight = 0;
         filterLayout.marginWidth = 0;
         filterParent.setLayout(filterLayout);
         filterParent.setFont(parent.getFont());
         filterParent.setLayoutData(new GridData(GridData.FILL_HORIZONTAL
                 | GridData.GRAB_HORIZONTAL));

         createFilterControl(filterParent);
            
         GridData data = new GridData(GridData.FILL_HORIZONTAL
                 | GridData.GRAB_HORIZONTAL);
         getFilterControl().setLayoutData(data);

         ToolBar toolBar = new ToolBar(filterParent, SWT.FLAT | SWT.HORIZONTAL);
         filterToolBar = new ToolBarManager(toolBar);

         createClearText(filterToolBar);

         filterToolBar.update(false);
         // initially there is no text to clear
 filterToolBar.getControl().setVisible(false);

         treeViewer = new TreeViewer(this, treeStyle);
         data = new GridData(GridData.FILL_BOTH);
         treeViewer.getControl().setLayoutData(data);
         treeViewer.getControl().addDisposeListener(new DisposeListener(){
             /* (non-Javadoc)
              * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent)
              */
             public void widgetDisposed(DisposeEvent e) {
                 refreshJob.cancel();
                 
             }
         });
         treeViewer.addFilter(patternFilter);
         
         createRefreshJob();
     }

     /**
      * Create the refresh job for the receiver.
      *
      */
     private void createRefreshJob() {
         refreshJob = new WorkbenchJob("Refresh Filter"){//$NON-NLS-1$
 /* (non-Javadoc)
              * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
              */
             public IStatus runInUIThread(IProgressMonitor monitor) {
                 if(treeViewer.getControl().isDisposed())
                     return Status.CANCEL_STATUS;
                 
                 String filterText = getFilterControlText();
                 boolean initial = initialText != null && filterText.equals(initialText);
                 if (initial) {
                     patternFilter.setPattern(null);
                 } else {
                     patternFilter.setPattern(getFilterControlText());
                 }
                 treeViewer.getControl().setRedraw(false);
                 treeViewer.refresh(true);
                 treeViewer.getControl().setRedraw(true);
                
                 if (filterText.length() > 0 && !initial) {
                     treeViewer.expandAll();
                     // enabled toolbar is a hint that there is text to clear
 // and the list is currently being filtered
 filterToolBar.getControl().setVisible(true);
                 } else {
                     // disabled toolbar is a hint that there is no text to clear
 // and the list is currently not filtered
 filterToolBar.getControl().setVisible(preferenceFilter != null);
                 }
                 return Status.OK_STATUS;
             }
             
         };
         refreshJob.setSystem(true);
     }

     /**
      * Create the filter control.
      */
     protected void createFilterControl(Composite parent) {
         filterText = new Text(parent, SWT.SINGLE | SWT.BORDER);
         filterText.getAccessible().addAccessibleListener(getAccessibleListener());
         getFilterControl().addKeyListener(getKeyListener());

     }

     /**
      * Get the key listener used for the receiver.
      * @return KeyListener
      */
     protected KeyAdapter getKeyListener() {
         return new KeyAdapter() {
                 
                 /*
                  * (non-Javadoc)
                  *
                  * @see org.eclipse.swt.events.KeyAdapter#keyReleased(org.eclipse.swt.events.KeyEvent)
                  */
                 public void keyReleased(KeyEvent e) {
                     // on a CR we want to transfer focus to the list
 if(e.keyCode == SWT.ARROW_DOWN)
                         treeViewer.getTree().setFocus();
                     else
                         textChanged();
                 }
             };
     }

     protected AccessibleAdapter getAccessibleListener() {
         return new AccessibleAdapter(){
             /* (non-Javadoc)
              * @see org.eclipse.swt.accessibility.AccessibleListener#getName(org.eclipse.swt.accessibility.AccessibleEvent)
              */
             public void getName(AccessibleEvent e) {
                 String filterTextString = getFilterText();
                 if(filterTextString.length() == 0){
                     e.result = initialText;
                 }
                 else
                     e.result = filterTextString;
             }
             
         };
     }

     /**
      * Get the text from the filter widget.
      * @return String
      */
     protected String getFilterText() {
         return filterText.getText();
     }
     /**
      * update the receiver after the text has changed
      */
     protected void textChanged() {
         refreshJob.schedule(200);
     }

     /**
      * Get the text from the filter control.
      * @return Text
      */
     protected String getFilterControlText() {
         return filterText.getText();
     }

     /**
      * Set the background for the widgets that support the filter text area
      *
      * @param background
      */
     public void setBackground(Color background) {
         super.setBackground(background);
         filterParent.setBackground(background);
         getFilterControl().setBackground(background);
         filterToolBar.getControl().setBackground(background);
     }

     /**
      * Create the button that clears the text.
      *
      * @param filterToolBar
      */
     private void createClearText(ToolBarManager filterToolBar) {

         IAction clearTextAction = new Action("", IAction.AS_PUSH_BUTTON) {//$NON-NLS-1$
 /*
              * (non-Javadoc)
              *
              * @see org.eclipse.jface.action.Action#run()
              */
             public void run() {
                 clearText();
             }
         };

         clearTextAction.setToolTipText(WorkbenchMessages.FilteredTree_ClearToolTip);
         clearTextAction.setImageDescriptor(JFaceResources.getImageRegistry()
                 .getDescriptor(CLEAR_ICON));
         clearTextAction.setDisabledImageDescriptor(JFaceResources
                 .getImageRegistry().getDescriptor(DCLEAR_ICON));

         filterToolBar.add(clearTextAction);
     }

     /**
      * clear the text in the filter text widget
      */
     protected void clearText() {
         setFilterText(""); //$NON-NLS-1$

         if(preferenceFilter != null){
             getViewer().removeFilter(preferenceFilter);
             preferenceFilter = null;
             getShell().setText(cachedTitle);
         }
         
         textChanged();
     }

     /**
      * Set the text in the filter area.
      * @param string
      */
     protected void setFilterText(String string) {
         filterText.setText(string);
         selectAll();
     }

     /**
      * Get the tree viewer associated with this control.
      *
      * @return the tree viewer
      */
     public TreeViewer getViewer() {
         return treeViewer;
     }

     /**
      * Get the filter text field associated with this control.
      *
      * @return the text field
      */
     public Text getFilterControl() {
         return filterText;
     }

     /**
      * Set the text that will be shown until the first focus.
      *
      * @param text
      */
     public void setInitialText(String text) {
         initialText = text;
         setFilterText(initialText);
         
         textChanged();
         listener = new FocusListener() {
             public void focusGained(FocusEvent event) {
                 filterFocusGained();
             }

             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.swt.events.FocusListener#focusLost(org.eclipse.swt.events.FocusEvent)
              */
             public void focusLost(FocusEvent e) {
                 filterFocusLost();
             }
         };
         getFilterControl().addFocusListener(listener);
     }

     

     protected void selectAll() {
         filterText.selectAll();
     }

     /**
      * Get the initial text for the receiver.
      * @return String
      */
     protected String getInitialText() {
         return initialText;
     }

     /**
      * Add the filter to the viewer.
      * @param filter
      */
     public void addFilter(PreferenceNodeFilter filter) {
         preferenceFilter = filter;
         getViewer().addFilter(filter);
         setInitialText(WorkbenchMessages.FilteredTree_FilteredMessage);
         
         if(getFilterControl() != null){
             setFilterText(WorkbenchMessages.FilteredTree_FilteredMessage);
             textChanged();
         }
         
         cachedTitle = getShell().getText();
         getShell().setText(
                 NLS.bind(
                         WorkbenchMessages.FilteredTree_FilteredDialogTitle,
                 cachedTitle));
         
     }

     /**
      * Focus has been gained on the filter control.
      *
      */
     protected void filterFocusGained() {
         selectAll();
         getFilterControl().removeFocusListener(listener);
     }

     /**
      * Focus has been lost on the filter control.
      *
      */
     protected void filterFocusLost() {
         
     }
     
 }

